package com.xqkj.commons.virtualdb.templates.impl;

import com.xqkj.commons.model.HandleResult;
import com.xqkj.commons.model.basicdo.BasicDo;
import com.xqkj.commons.virtualdb.model.*;
import com.xqkj.commons.virtualdb.service.functions.*;
import com.xqkj.commons.virtualdb.templates.AddModelToVirtualDb;

import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author lwt<br>
 * @description <br>
 * @date 2020/1/16
 */
public class DefAddModelToVirtualDb<P extends BasicDo> implements AddModelToVirtualDb<P> {



    //
    private Function<P,Integer> insertModelFunction;
    private Function<UnionIdxMappingSvModel,Integer> insertUidxMappingFunction;
    private Function<List<UnionIdxMappingSvModel>,Integer> batchInsertUidxMappingFunction;
    private Function<GetByUnionIdxParameterModel,P> getByMainUidxFunction;
    private Function<GetByUnionIdxParameterModel,P> getByAttributeUidxFunction;
    private Function<String,Boolean> lockFunction;
    private Consumer<String> unLockFunction;
    //
    private Function<ParametersWrappedModel<P>, Long> pkIdGenerater;
    private Function<ParametersWrappedModel<P>,VirtualDbLockInfoModel> lockInfoGetter;
    private Function<VirtualDbLockInfoModel, HandleResult<P>> dbModelGetter;
    private Function<VirtualDbLockInfoModel, VirtualDbLockResultModel> lockAllFunction;
    private Consumer<VirtualDbLockInfoModel> unLockAllFunction;
    private Function<P,HandleResult<Integer>> modelInfoWriter;
    private Function<VirtualDbLockInfoModel,HandleResult<String>> unionTmpMappingWriter;
    //
    private OperateModelToVirtualDbHelper<P> operateModelToVirtualDbHelper;
    //
    private boolean isInit=false;

    public DefAddModelToVirtualDb(){

    }

    public void init(){
        if(isInit){
            return;
        }
        lockInfoGetter=new LockInfoGetter<>();
        pkIdGenerater=new PkIdGenerater<>();
        unionTmpMappingWriter=new UnionTmpMappingWriter(insertUidxMappingFunction,batchInsertUidxMappingFunction);
        dbModelGetter=new DbModelGetter<>(getByMainUidxFunction,getByAttributeUidxFunction);
        lockAllFunction=new LockAllFunction(lockFunction);
        unLockAllFunction=new UnLockAllFunction(unLockFunction);
        modelInfoWriter=new ModelInfoWriter<>(insertModelFunction);
        //
        operateModelToVirtualDbHelper=new OperateModelToVirtualDbHelper();
        operateModelToVirtualDbHelper.setDbModelGetter(dbModelGetter);
        operateModelToVirtualDbHelper.setLockAllFunction(lockAllFunction);
        operateModelToVirtualDbHelper.setUnLockAllFunction(unLockAllFunction);
        isInit=true;
    }

    @Override
    public HandleResult<VirtualDbNormalShadingKeysModel> insertModel(P model, Long tenantId, String tenantExtCode,
                                                                     Map<String, ModelBasicAttributeSvModel> codeToAttributeMap,
                                                                     VirtualDbAttributeIdxConfigModel attributeIdxConfig) {
        //参数包装
        ParametersWrappedModel<P> parametersWrappedModel = new ParametersWrappedModel();
        parametersWrappedModel.setParameter(model);
        parametersWrappedModel.setTenantId(tenantId);
        parametersWrappedModel.setTenantExtCode(tenantExtCode);
        parametersWrappedModel.setAttributeIdxConfig(attributeIdxConfig);
        parametersWrappedModel.setCodeToAttributeMap(codeToAttributeMap);
        //1.获取所有需要锁住的唯一性索引
        VirtualDbLockInfoModel virtualDbLockInfoModel = lockInfoGetter.apply(parametersWrappedModel);
        if(!virtualDbLockInfoModel.isNeedLock()){
            return insertModelWithOutLock(parametersWrappedModel);
        }
        return insertModelWithLock(parametersWrappedModel,virtualDbLockInfoModel);
    }

    /**
     *
     * @param parametersWrapped
     * @return
     */
    private HandleResult<VirtualDbNormalShadingKeysModel> insertModelWithOutLock(ParametersWrappedModel<P> parametersWrapped){
        //4.获取并设置分布式主键
        long pkId=pkIdGenerater.apply(parametersWrapped);
        parametersWrapped.getParameter().setPkId(pkId);
        //6.将数据写入相关数据表中--事务性写入
        HandleResult<Integer> modelWriteResult=modelInfoWriter.apply(parametersWrapped.getParameter());
        if(!modelWriteResult.isSuccess()){
            return HandleResult.failed(modelWriteResult.getCode(),modelWriteResult.getMsg());
        }
        VirtualDbNormalShadingKeysModel shadingKeysModel=new VirtualDbNormalShadingKeysModel();
        shadingKeysModel.setPkId(pkId);
        shadingKeysModel.setTenantId(parametersWrapped.getTenantId());
        return HandleResult.success(shadingKeysModel);
        //
    }


    private HandleResult<VirtualDbNormalShadingKeysModel> insertModelWithLock(ParametersWrappedModel<P> parametersWrappedModel,
                                                                              VirtualDbLockInfoModel virtualDbLockInfoModel){
        return operateModelToVirtualDbHelper.lockAddMappingAndExecuteOperation(
                parametersWrappedModel,virtualDbLockInfoModel,
                (parametersWrapped,virtualDbLockInfo)->
                        handleUnionTmpMapping(parametersWrapped,virtualDbLockInfo),
                (parametersWrapped)->
                        doInsertModel(parametersWrapped));
    }

    private HandleResult<String> handleUnionTmpMapping(ParametersWrappedModel<P> parametersWrapped, VirtualDbLockInfoModel virtualDbLockInfoModel) {
        //4.获取并设置分布式主键
        long pkId=pkIdGenerater.apply(parametersWrapped);
        parametersWrapped.getParameter().setPkId(pkId);
        virtualDbLockInfoModel.setPkId(pkId);
        //5.写唯一性索引到反查表中--事务性写入
        HandleResult<String> mappingWriteResult=unionTmpMappingWriter.apply(virtualDbLockInfoModel);
        return mappingWriteResult;
    }

    private HandleResult<VirtualDbNormalShadingKeysModel> doInsertModel(ParametersWrappedModel<P> parametersWrapped){
        HandleResult<Integer> modelWriteResult=modelInfoWriter.apply(parametersWrapped.getParameter());
        if(!modelWriteResult.isSuccess()){
            return HandleResult.failed(modelWriteResult.getCode(),modelWriteResult.getMsg());
        }
        VirtualDbNormalShadingKeysModel shadingKeysModel=new VirtualDbNormalShadingKeysModel();
        shadingKeysModel.setPkId(parametersWrapped.getParameter().getPkId());
        shadingKeysModel.setTenantId(parametersWrapped.getTenantId());
        return HandleResult.success(shadingKeysModel);
    }

    /////////////////////////////////////////////
    public void setInsertModelFunction(Function<P, Integer> insertModelFunction) {
        this.insertModelFunction = insertModelFunction;
    }

    public void setInsertUidxMappingFunction(Function<UnionIdxMappingSvModel, Integer> insertUidxMappingFunction) {
        this.insertUidxMappingFunction = insertUidxMappingFunction;
    }

    public void setBatchInsertUidxMappingFunction(Function<List<UnionIdxMappingSvModel>, Integer> batchInsertUidxMappingFunction) {
        this.batchInsertUidxMappingFunction = batchInsertUidxMappingFunction;
    }

    public void setGetByMainUidxFunction(Function<GetByUnionIdxParameterModel, P> getByMainUidxFunction) {
        this.getByMainUidxFunction = getByMainUidxFunction;
    }

    public void setGetByAttributeUidxFunction(Function<GetByUnionIdxParameterModel, P> getByAttributeUidxFunction) {
        this.getByAttributeUidxFunction = getByAttributeUidxFunction;
    }

    public void setLockFunction(Function<String, Boolean> lockFunction) {
        this.lockFunction = lockFunction;
    }

    public void setUnLockFunction(Consumer<String> unLockFunction) {
        this.unLockFunction = unLockFunction;
    }
}
